#include "stdafx.h"
#include "Toolbar.h"
#include "ToolbarComServer.h"

//****************************************************************************
// CToolbarComServer
//****************************************************************************

CToolbarComServer::CToolbarComServer()
    : m_hwndSiteWindow(NULL)
    , m_toolbarOwner()
    , m_dwBandID(0)
    , m_dwViewMode(0)
    , m_inputObjectSite(NULL)
    , m_pIWebBrowser2(NULL)
    , m_pIOleCommandTarget(NULL)
{
    // TODO: get this title from a configuration file.
    m_toolbarTitle = L"Gamers' Web";
}

CToolbarComServer::~CToolbarComServer()
{
}

BOOL CToolbarComServer::CreateToolbarWindow()
{
    RECT windowRect = {0};
    ::GetClientRect(m_hwndSiteWindow, &windowRect);
    m_toolbarOwner.Create(m_hwndSiteWindow, windowRect, NULL, 0, 0, 0, m_pIWebBrowser2);
    ATLASSERT(m_toolbarOwner.m_hWnd != NULL);
    return m_toolbarOwner.IsWindow();
}

void CToolbarComServer::FinalRelease()
{
    ReleaseResources();
}

void CToolbarComServer::ReleaseResources()
{
    if (m_inputObjectSite)
    {
        m_inputObjectSite->Release();
        m_inputObjectSite = NULL;
    }

    if (m_pIWebBrowser2)
    {
        m_pIWebBrowser2->Release();
        m_pIWebBrowser2 = NULL;
    }

    if (m_pIOleCommandTarget)
    {
        m_pIOleCommandTarget->Release();
        m_pIOleCommandTarget = NULL;
    }

    m_hwndSiteWindow = NULL;

    if (m_toolbarOwner.IsWindow())
    {
        m_toolbarOwner.DestroyWindow();
    }
}

//****************************************************************************
// IObjectWithSite implementation
//****************************************************************************

STDMETHODIMP CToolbarComServer::SetSite(IUnknown* punkSite)
{
    ReleaseResources();

    // If punkSite is not NULL, a new site is being set.
    if (punkSite)
    {
        // Get the parent window.
        IOleWindow  *pOleWindow = NULL;
        if (SUCCEEDED(punkSite->QueryInterface(IID_IOleWindow, (LPVOID*)&pOleWindow)) && pOleWindow)
        {
            pOleWindow->GetWindow(&m_hwndSiteWindow);
            pOleWindow->Release();
        }

        if (!m_hwndSiteWindow)
        {
            ReleaseResources();
            return E_FAIL;
        }

        HRESULT hr = S_OK;
        hr = punkSite->QueryInterface(IID_IInputObjectSite, (LPVOID*)&m_inputObjectSite);
        if (FAILED(hr) || !m_inputObjectSite)
        {
            ReleaseResources();
            return E_FAIL;
        }  
        
        hr = punkSite->QueryInterface(IID_IOleCommandTarget, (LPVOID*)&m_pIOleCommandTarget);
        if (FAILED(hr) || !m_pIOleCommandTarget)
        {
            ReleaseResources();
            return E_FAIL;
        }
        
        IServiceProvider* pSP = NULL;
        hr = punkSite->QueryInterface(IID_IServiceProvider, (LPVOID*)&pSP);
        if (FAILED(hr) || !pSP)
        {
            ReleaseResources();
            return E_FAIL;
        }

        hr = pSP->QueryService(SID_SWebBrowserApp, IID_IWebBrowser2, (LPVOID*)&m_pIWebBrowser2);
        pSP->Release();

        if (FAILED(hr) || !m_pIWebBrowser2)
        {
            ReleaseResources();
            return E_FAIL;
        }

        if (!CreateToolbarWindow())
        {
            ReleaseResources();
            return E_FAIL;
        }
    }

    return S_OK;

}


STDMETHODIMP CToolbarComServer::GetSite(REFIID riid, void **ppvReturn)
{
    *ppvReturn = NULL;

    if(m_inputObjectSite)
        return m_inputObjectSite->QueryInterface(riid, ppvReturn);

    return E_FAIL;
}

//****************************************************************************
// IDeskBand
//****************************************************************************
/*
ptMinSize
The POINTL structure that receives the minimum size of the band object. 
The minimum width is placed in the x member and the minimum height is placed in the y member. 

ptMaxSize
The POINTL structure that receives the maximum size of the band object. 
The maximum height is placed in the y member and the x member is ignored. 
If there is no limit for the maximum height, (LONG)-1 should be used. 

ptIntegral
The POINTL structure that receives the sizing step value of the band object. 
The vertical step value is placed in the y member, and the x member is ignored. 
The step value determines in what increments the band will be resized. 
This member is ignored if dwModeFlags does not contain DBIMF_VARIABLEHEIGHT. 

ptActual
The POINTL structure that receives the ideal size of the band object. 
The ideal width is placed in the x member and the ideal height is placed in the y member. 
The band container will attempt to use these values, but the band is not guaranteed to be this size. 
*/

STDMETHODIMP CToolbarComServer::GetBandInfo(DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO *pdbi)
{
    if (pdbi)
    {
        m_dwBandID = dwBandID;
        m_dwViewMode = dwViewMode;

        // TODO: need to calculate these values by the button's desired image and text size
        LONG buttonHeight = 22;
        LONG buttonWidth  = 250;

        if (pdbi->dwMask & DBIM_MINSIZE)
        {
            pdbi->ptMinSize.x = buttonWidth;
            pdbi->ptMinSize.y = buttonHeight;
        }

        if (pdbi->dwMask & DBIM_MAXSIZE)
        {
            pdbi->ptMaxSize.x = 0;			// ignored
            pdbi->ptMaxSize.y = buttonHeight;
        }

        if (pdbi->dwMask & DBIM_INTEGRAL)
        {
            pdbi->ptIntegral.x = 1;
            pdbi->ptIntegral.y = 1;
        }

        if (pdbi->dwMask & DBIM_ACTUAL)
        {
            pdbi->ptActual.x = buttonWidth;
            pdbi->ptActual.y = buttonHeight;
        }

        if (pdbi->dwMask & DBIM_TITLE)
        {
            // length - Specifies the size, in bytes, of the block of memory to fill with zeros.
            ::ZeroMemory(pdbi->wszTitle, sizeof(pdbi->wszTitle));

            // count - Number of characters to be copied
            ::wcsncpy(pdbi->wszTitle, m_toolbarTitle, (sizeof(pdbi->wszTitle)/sizeof(WCHAR))-1);
        }

        if (pdbi->dwMask & DBIM_MODEFLAGS)
        {
            pdbi->dwModeFlags = DBIMF_VARIABLEHEIGHT;
        }

        if (pdbi->dwMask & DBIM_BKCOLOR)
        {
            // Use the default background color by removing this flag.
            pdbi->dwMask &= ~DBIM_BKCOLOR;
        }

        return S_OK;
    }

    return E_INVALIDARG;

}

//****************************************************************************
// IDockingWindow: The IDeskBand interface inherits from IDockingWindow
//****************************************************************************

STDMETHODIMP CToolbarComServer::ShowDW(BOOL fShow)
{
    m_toolbarOwner.GetToolbarWnd().ShowWindow(fShow ? SW_SHOW : SW_HIDE);
    return S_OK;
}

STDMETHODIMP CToolbarComServer::CloseDW(DWORD dwReserved)
{
    //	Don't destroy the window, only close it. 
    ShowDW(FALSE);
    return S_OK;
}

STDMETHODIMP CToolbarComServer::ResizeBorderDW(LPCRECT prcBorder, IUnknown *punkToolbarSite, BOOL fReserved)
{
    // From the MSDN resources referenced in the header file:
    // "The ResizeBorderDW method is not used with any type of band object and should always return E_NOTIMPL."
    return E_NOTIMPL;
}

//****************************************************************************
// IOleWindow: The IDockingWindow interface inherits from IOleWindow. 
//****************************************************************************

STDMETHODIMP CToolbarComServer::GetWindow(HWND *phwnd)
{
    HRESULT hr = E_INVALIDARG;
    if (phwnd)
    {
        *phwnd = m_toolbarOwner.GetToolbarWnd().m_hWnd;
        hr = S_OK;
    }
    return hr;
}

STDMETHODIMP CToolbarComServer::ContextSensitiveHelp(BOOL fEnterMode)
{
    // Context-sensitive Help is not implemented, so IOleWindow::ContextSensitiveHelp returns E_NOTIMPL
    return E_NOTIMPL;
}

//****************************************************************************
// IInputObject
//****************************************************************************

// EnumChildWindows continues until the last child window is enumerated or the callback function returns FALSE
BOOL CALLBACK EnumToolbarChildsProc(HWND hwnd, LPARAM lParam)
{
    BOOL* pbHasFocus = (BOOL*)lParam;
    *pbHasFocus = (::GetFocus() == hwnd);
    return (!*pbHasFocus);
}

STDMETHODIMP CToolbarComServer::HasFocusIO()
{
    BOOL bHasFocus = FALSE;
    EnumChildWindows(m_toolbarOwner.GetToolbarWnd().m_hWnd, EnumToolbarChildsProc, reinterpret_cast<LPARAM>(&bHasFocus)); // MSDN: "Return value not used"
    return bHasFocus ? S_OK : S_FALSE ;
}

STDMETHODIMP CToolbarComServer::TranslateAcceleratorIO(LPMSG lpMsg)
{
    // Returns S_OK if the accelerator was translated, or S_FALSE otherwise.
    return S_FALSE ;
}

STDMETHODIMP CToolbarComServer::UIActivateIO(BOOL fActivate, MSG *pMsg)
{
    if (fActivate)
    {
        m_toolbarOwner.GetToolbarWnd().SetFocus();
    }
    return S_OK;
}


